use super::error::LitResult;

use crate::byte::*;

#[derive(Debug)]
pub struct LiteralSectionHeader {
    literals_block_type:LiteralsBlockType,
    size_format:SizeFormat,
    generated_size:u32,
    compressed_size:Option<u32>,
    single_stream_flag:bool,
}

impl LiteralSectionHeader {
    pub fn serialize_into<W:ByteWrite>(&self, writer:&mut ByteWriter<W>) -> LitResult<()> {
        let first_byte =  match (self.literals_block_type,self.size_format) {
            (LiteralsBlockType::RawLiteralsBlock|LiteralsBlockType::RLELiteralsBlock,
                SizeFormat::B00|SizeFormat::B10) => 
                self.literals_block_type.value() |
                self.size_format.value() |
                (self.generated_size << 3) as u8,
            _ => 
                self.literals_block_type.value() |
                self.size_format.value() |
                ((self.generated_size & 0xf) << 4) as u8,
        };
        writer.write_from_u8(first_byte)?;
        todo!()
    }

    pub fn deserialize_from<R:ByteRead>(total_len:&mut u32, reader:&mut ByteReader<R>) -> LitResult<Self> {
        let first_byte = reader.read_to_u8()?;
        let literals_block_type = (first_byte & 0x3).into();
        let size_format = (first_byte & 0b1100).into();
        let ((generated_size, compressed_size),single_stream_flag) = match (literals_block_type,size_format) {
            (LiteralsBlockType::RawLiteralsBlock|LiteralsBlockType::RLELiteralsBlock,
                SizeFormat::B00|SizeFormat::B10) => { *total_len -= 1; ((Self::deserialize_only_generated_size_small(first_byte),None),true) },
            (LiteralsBlockType::RawLiteralsBlock|LiteralsBlockType::RLELiteralsBlock,
                SizeFormat::B01) => { *total_len -= 2; ((Self::deserialize_only_generated_size_medium(first_byte,reader)?,None),true) },
            (LiteralsBlockType::RawLiteralsBlock|LiteralsBlockType::RLELiteralsBlock,
                SizeFormat::B11) => { *total_len -= 3; ((Self::deserialize_only_generated_size_large(first_byte,reader)?,None),true) },
            (LiteralsBlockType::CompressedLiteralsBlock|LiteralsBlockType::TreelessLiteralsBlock,
                SizeFormat::B00) => { *total_len -= 3; (Self::deserialize_both_small(first_byte,reader)?,true) },
            (LiteralsBlockType::CompressedLiteralsBlock|LiteralsBlockType::TreelessLiteralsBlock,
                SizeFormat::B01) => { *total_len -= 3; (Self::deserialize_both_small(first_byte,reader)?,false) },
            (LiteralsBlockType::CompressedLiteralsBlock|LiteralsBlockType::TreelessLiteralsBlock,
                SizeFormat::B10) => { *total_len -= 4; (Self::deserialize_both_meduim(first_byte,reader)?,false) },
            (LiteralsBlockType::CompressedLiteralsBlock|LiteralsBlockType::TreelessLiteralsBlock,
                SizeFormat::B11) => { *total_len -= 5; (Self::deserialize_both_large(first_byte,reader)?,false) },
        };
        Ok(Self { literals_block_type, size_format, generated_size, compressed_size, single_stream_flag})
    }

    fn deserialize_only_generated_size_small(first_byte:u8) -> u32 {
        first_byte as u32 >> 5
    }

    fn deserialize_only_generated_size_medium<R:ByteRead>(first_byte:u8, reader:&mut ByteReader<R>) -> LitResult<u32> {
        let second_byte = reader.read_to_u8()?;
        Ok(((second_byte as u32) << 4) | (first_byte as u32 >> 4))
    }

    fn deserialize_only_generated_size_large<R:ByteRead>(first_byte:u8, reader:&mut ByteReader<R>) -> LitResult<u32> {
        let mut two_bytes = [0u8;2]; 
        reader.read_to_bytes(&mut two_bytes)?;
        Ok(((two_bytes[1] as u32) << 4) | ((two_bytes[0] as u32) << 4) | (first_byte as u32 >> 4))
    }

    fn deserialize_both_small<R:ByteRead>(first_byte:u8, reader:&mut ByteReader<R>) -> LitResult<(u32,Option<u32>)> {
        let mut two_bytes = [0u8;2]; 
        reader.read_to_bytes(&mut two_bytes)?;
        Ok((
            (((two_bytes[0] & 0x3f) as u32) << 4) | (first_byte as u32 >> 4),
            Some(((two_bytes[1] as u32) << 2) | (((two_bytes[0] >> 6) as u32)))
        ))
    }

    fn deserialize_both_meduim<R:ByteRead>(first_byte:u8, reader:&mut ByteReader<R>) -> LitResult<(u32,Option<u32>)> {
        let mut three_bytes = [0u8;3]; 
        reader.read_to_bytes(&mut three_bytes)?;
        Ok((
            (((three_bytes[1] & 0x3) as u32) << 12) | ((three_bytes[0] as u32) << 4) | (first_byte as u32 >> 4),
            Some(((three_bytes[2] as u32) << 6) | (((three_bytes[1] >> 2) as u32)))
        ))
    }

    fn deserialize_both_large<R:ByteRead>(first_byte:u8, reader:&mut ByteReader<R>) -> LitResult<(u32,Option<u32>)> {
        let mut four_bytes = [0u8;4]; 
        reader.read_to_bytes(&mut four_bytes)?;
        Ok((
            (((four_bytes[1] & 0x3f) as u32) << 12) | ((four_bytes[0] as u32) << 4) | (first_byte as u32 >> 4),
            Some(((four_bytes[3] as u32) << 10) |((four_bytes[2] as u32) << 2) | (((four_bytes[1] >> 6) as u32)))
        ))
    }

    pub fn literals_block_type(&self) -> LiteralsBlockType {
        self.literals_block_type
    }

    pub fn generated_size(&self) -> u32 {
        self.generated_size
    }

    pub fn compressed_size(&self) -> Option<u32> {
        self.compressed_size
    }

    pub fn literal_section_content_size(&self) -> u32 {
        match self.literals_block_type {
            LiteralsBlockType::RawLiteralsBlock => self.generated_size,
            LiteralsBlockType::RLELiteralsBlock => 1,
            LiteralsBlockType::CompressedLiteralsBlock => self.compressed_size.unwrap(),
            LiteralsBlockType::TreelessLiteralsBlock => self.compressed_size.unwrap(),
        }
    }

    pub fn stream_num(&self) -> u8 {
        if self.single_stream_flag { 1 } else { 4 }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum LiteralsBlockType {
    RawLiteralsBlock,
    RLELiteralsBlock,
    CompressedLiteralsBlock,
    TreelessLiteralsBlock,
}

impl LiteralsBlockType {
    pub fn value(self) -> u8 {
        match self {
            Self::RawLiteralsBlock => 0,
            Self::RLELiteralsBlock => 1,
            Self::CompressedLiteralsBlock => 2,
            Self::TreelessLiteralsBlock => 3,
        }
    }
}

impl From<u8> for LiteralsBlockType {
    fn from(value: u8) -> Self {
        match value {
            0 => Self::RawLiteralsBlock,
            1 => Self::RLELiteralsBlock,
            2 => Self::CompressedLiteralsBlock,
            3 => Self::TreelessLiteralsBlock,
            _ => panic!("invalid num to literals block type"),
        }
    }
}


#[derive(Debug, Clone, Copy)]
pub enum SizeFormat {
    B00,
    B01,
    B10,
    B11,
}

impl SizeFormat {
    pub fn value(self) -> u8 {
        match self {
            Self::B00 => 0b0000,
            Self::B01 => 0b0100,
            Self::B10 => 0b1000,
            Self::B11 => 0b1100,
        }
    }
}

impl From<u8> for SizeFormat {
    fn from(value: u8) -> Self {
        match value {
            0b0000 => Self::B00,
            0b0100 => Self::B01,
            0b1000 => Self::B10,
            0b1100 => Self::B11,
            _ => panic!("invalid num to literals block type"),
        }
    }
}